// Copyright 2023 Guo Tingjin(ericple)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import HAPV_CONSTANTS from "../hapvConstants";
import parseResrouceKey from "../utils/parseResourceKey";
import HapInfo from "./hapInfo";
import path from 'path';
import fs from 'fs';
import HapAbility from "./ability";
import HapExtAbility from "./extAbility";
import OutInfo from "./outInfo";
import { appNameFilterRegex, appNameRegex } from "../utils/regGener";
import hex2Arr from "../utils/hex2Arr";
import readMap from "../utils/readMap";
import analysePermission from "../utils/analysePermission";

class HapInstance {
    hap: HapInfo = new HapInfo;
    root: string = '';
    out: OutInfo = new OutInfo;
    resourceBuffer: Buffer;
    args: Map<string, string>;
    constructor(bundle: HapInfo, rootPath: string, resourceBuffer: Buffer, args: Map<string, string>) {
        this.hap = bundle;
        this.root = rootPath;
        this.resourceBuffer = resourceBuffer;
        this.args = args;
        this.grabInfo();
    }
    grabInfo() {
        let debug = this.args.has(HAPV_CONSTANTS.NAMED_ARGUMENTS.DEBUG_FLAG);
        const resourceStr = this.resourceBuffer.toString('hex');
        const mainAbility = this.getMainAbility();
        if (debug) console.log(mainAbility)
        const appNameArray = resourceStr.match(appNameRegex(parseResrouceKey(mainAbility.label), parseResrouceKey(mainAbility.description)));
        if (!appNameArray) {
            console.error('Error: fail to parse app name, the hap file maybe broken.(28)');
            process.exit(-1);
        }
        if (debug) console.log('appNameArray', appNameArray);
        const filteredAppNameArray = appNameArray[appNameArray.length - 1].split(appNameFilterRegex(parseResrouceKey(mainAbility.label)));
        if (debug) console.log('filteredAppNameArray', filteredAppNameArray);
        if (!filteredAppNameArray) {
            console.error('Error: fail to parse app name, the hap file maybe broken.(33)');
            process.exit(-1);
        }
        const explodedName = filteredAppNameArray[filteredAppNameArray.length - 2];
        if (debug) console.log('explodedName', explodedName)

        this.out.appName = Buffer.from(hex2Arr(explodedName)).toString();
        this.out.bundleName = this.hap.app.bundleName;
        this.out.versionName = this.hap.app.versionName;
        this.out.versionNumber = this.hap.app.versionCode;
        this.out.compileTarget = this.hap.app.targetAPIVersion;
        this.out.vendor = this.hap.app.vendor;
        if (this.hap.module.requestPermissions) {
            this.out.requestPermissionNumber = this.hap.module.requestPermissions.length;
            this.out.requestedPermissions = this.hap.module.requestPermissions;
            analysePermission(this.out.requestedPermissions);
        }
    }
    processArg(args: Map<string, string>) {
        if (args.has(HAPV_CONSTANTS.NAMED_ARGUMENTS.SHOW_INFO)) {
            console.log(this.out);
        }
        if (args.has(HAPV_CONSTANTS.NAMED_ARGUMENTS.GET_ICON)) {
            const src = this.getAppIcon();
            const target = path.join(readMap(args, HAPV_CONSTANTS.NAMED_ARGUMENTS.WORK_DIR), path.basename(src))
            fs.copyFileSync(src, target);
            console.log(`App icon has been generated at: ${target}`);
        }
        if(args.has(HAPV_CONSTANTS.NAMED_ARGUMENTS.OUTPUT) && args.get(HAPV_CONSTANTS.NAMED_ARGUMENTS.OUTPUT)) {
            const target = readMap(args, HAPV_CONSTANTS.NAMED_ARGUMENTS.OUTPUT, path.join(this.root, '..'));
            fs.writeFileSync(target, JSON.stringify(this.out));
        }
        if(!args.has(HAPV_CONSTANTS.NAMED_ARGUMENTS.SAVE_CACHE)) this.clearCache()
    }
    private getMainAbility() {
        const mainAbility = this.hap.module.mainElement;
        let index = 0;
        if (this.hap.module.abilities) {
            while (this.hap.module.abilities[index].name != mainAbility && index < this.hap.module.abilities.length) {
                index++;
            }
            return this.hap.module.abilities[index];
        } else if (this.hap.module.extensionAbilities) {
            while (this.hap.module.extensionAbilities[index].name != mainAbility && index < this.hap.module.extensionAbilities.length) {
                index++;
            }
            return this.hap.module.extensionAbilities[index];
        }
        console.error('Error: hap file maybe broken, cannot read ability list.');
        process.exit(-1);
    }
    public getAppIcon(mainAbility: HapAbility | HapExtAbility = this.getMainAbility(), root: string = this.root) {
        let iconFileName = parseResrouceKey(mainAbility.icon);
        const mediaRoot = path.join(root, 'resources', 'base', 'media');
        let mediaFiles = fs.readdirSync(mediaRoot);
        let i = 0;
        while (i < mediaFiles.length) {
            if (mediaFiles[i].startsWith(iconFileName)) {
                iconFileName = mediaFiles[i];
                break;
            }
            i++;
        }
        return path.join(root, 'resources', 'base', 'media', iconFileName);
    }
    public clearCache() {
        fs.rmSync(this.root, { recursive: true, force: true });
    }
}

export default HapInstance;